package edu.hawaii.duedates.configuration;

import edu.hawaii.duedates.datastore.DueDatesConfig;
import edu.hawaii.duedates.configuration.jaxb.DueDates;
import edu.hawaii.duedates.configuration.jaxb.Libraries;
import edu.hawaii.duedates.configuration.jaxb.User;
import edu.hawaii.duedates.libraries.HawaiiStateLibrary;
import edu.hawaii.duedates.libraries.ILibrary;
import edu.hawaii.duedates.libraries.UhLibrary;
import java.io.File;
import java.lang.reflect.Constructor;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

/**
 * Provides access to the contents of the DueDates configuration file. The default DueDates
 * configuration file is stored in ~/.duedates/duedates.xml.
 * 
 * @author Arthur Shum, Daniel Tian, Daniel Arakaki, Jeho Jung,
 */
public class ConfigurationManager {

  /** A JAXB DueDates object to hold information read in from the configuration file. */
  private DueDates dueDates;

  /**
   * Creates a new ConfigurationManager to read in the DueDates configuration file.
   * 
   * @param configFile An XML file containing DueDates configuration information.
   * @param schemaFile The XML Schema Definition (XSD) file for the XML file.
   */
  public ConfigurationManager(File configFile, File schemaFile) {
    try {
      JAXBContext jaxbContext = JAXBContext
          .newInstance(edu.hawaii.duedates.configuration.jaxb.ObjectFactory.class);
      if (configFile.exists() && schemaFile.exists()) {
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        SchemaFactory schemaFactory = SchemaFactory
            .newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = schemaFactory.newSchema(schemaFile);
        unmarshaller.setSchema(schema);
        this.dueDates = (DueDates) unmarshaller.unmarshal(configFile);
      }
      else {
        throw new Exception("XML or XSD file doesn't exist!");
      }
    }
    catch (UnmarshalException u) {
      throw new RuntimeException("Invalid XML file!", u);
    }
    catch (Exception e) {
      // CHANGE ME?
      throw new RuntimeException("Error in ConfigurationManager", e);
    }
  }

  /**
   * Create a new ConfigurationManager that provides access to the DueDates configuration file in
   * ~/.duedates/duedates.xml.
   */
  public ConfigurationManager() {
    this(new File(System.getProperty("user.home") + "/.duedates/duedates.xml"), 
        new File(System.getProperty("user.home") + "/.duedates/duedates.definition.xsd"));
  }

  /**
   * Reads the DueDates configuration file and stores it in an instance of DueDatesConfig.  All
   * classes refer to JAXB-generated classes unless explicitly specified.
   * 
   * @return a DueDatesConfig object, which stores the information read in from the DueDates 
   * configuration file.
   */
  @SuppressWarnings("unchecked")
  public DueDatesConfig getDueDatesConfig() {
    DueDatesConfig dueDateConfig = new DueDatesConfig();

    dueDateConfig.setSmtpServer(this.dueDates.getSmtpServer());

    // for each <User> in the XML file
    for (User xUser : this.dueDates.getUsers().getUser()) {
      // create a User object
      edu.hawaii.duedates.datastore.User user = new edu.hawaii.duedates.datastore.User
          (xUser.getName(), xUser.getEmail(), xUser.getPassword());
          
      // for each <User.Library> in the XML file
      for (User.Library xULibrary : xUser.getLibrary()) {
        
        // the Library to be added to User.libraryList
        edu.hawaii.duedates.libraries.ILibrary library = null;
        
        if (xULibrary.getName().equals("University of Hawaii")) {
          library = new UhLibrary(xULibrary.getUsername(), xULibrary.getPassword());
        }
        else if (xULibrary.getName().equals("Hawaii State Library")) {
          library = new HawaiiStateLibrary(xULibrary.getUsername(), xULibrary.getPassword());
        }
        // if XML file does not contain <Libraries> element
        // then <User.Library.name> in the XML file doesn't match anything
        else if (this.dueDates.getLibraries() == null) {
        //if (this.dueDates.getLibraries() == null) {
          throw new RuntimeException("Unrecognized library: " + xULibrary.getName());
        }
        // XML file does contain <Libraries> element
        // try to find a match
        else {
          // for each <Libraries.Library> in the XML file
          for (Libraries.Library xLLibrary : this.dueDates.getLibraries().getLibrary()) {
            // if <User.Library.name> == <Libraries.Library.name>
            if (xULibrary.getName().equals(xLLibrary.getName())) {
              // instantiate the class
              try {
                // Library, not JAXB Library
                Class<ILibrary> lib = (Class<ILibrary>) Class.forName(xLLibrary.getClazz());
                // library = lib.newInstance();
                Constructor<ILibrary> construc = lib.getConstructor();
                library = construc.newInstance();
                library.setUsername(xULibrary.getUsername());
                library.setPassword(xULibrary.getPassword());
              }
              catch (Exception e) {
                throw new RuntimeException("Error instantiating class: " 
                    + xLLibrary.getClazz(), e);
              }
            } 
            // <User.Library.name> in the XML file doesn't match anything
            else {
              throw new RuntimeException("Unrecognized library: " + xULibrary.getName());
            }
          } // for
        } // else
        // add the library to the user's list of libraries
        user.addLibrary(library);
      } // inner for loop
      // add the user to the list of users
      dueDateConfig.addUser(user);
    } // outer for loop
    return dueDateConfig;
  }
  
  /*
   * For manual testing.
   * @param args Unused.
   */
  /*
  public static void main(String[] args) {
    File xmlfile = new File(System.getProperty("user.dir") + "/xml/test.xml");
    File xsdfile = new File(System.getProperty("user.dir") + "/xml/duedates.definition.xsd");    
    ConfigurationManager cm = new ConfigurationManager(xmlfile, xsdfile);
    cm.getDueDatesConfig();
    System.out.println("success");
  }
  */
  
}
